﻿using UnityEngine;
using SocketWeaver.FixedMath;

namespace SocketWeaver.FPhysics2D
{
    [RequireComponent(typeof(FTransform))]
    [ExecuteInEditMode]
    public abstract class FCollider2D : MonoBehaviour
    {
        protected FPhysicsShape2D _shape;
        protected FPhysicsFixture2D _fixture;
        protected FTransform _fTransform;
        protected FRigidbody2D _attachedBody;

        protected GameObject _root;

        [SerializeField]
        bool _isTrigger = false;

        [SerializeField]
        FFloat _friction = FFloat.FromDivision(4, 10);

        [SerializeField]
        FFloat _bounciness = FFloat.FromDivision(2, 10);

        [SerializeField]
        internal FFloat _density = FFloat.FromDivision(1, 1);

        [SerializeField]
        internal bool _overrideMass = false;

        [SerializeField]
        internal FFloat _customMass = FFloat.zero;

        [SerializeField]
        byte _fixtureId;

        public int fixtureId
        {
            get
            {
                return _fixtureId;
            }
        }

        public FRigidbody2D attachedBody
        {
            get
            {
                return _attachedBody;
            }
        }

        [SerializeField]
        bool _useSpriteRendererSize = false;

        [SerializeField]
        FVector2 _spriteRendererSize = FVector2.one;

        protected FVector3 colliderScale
        {
            get
            {
                if (_useSpriteRendererSize)
                {
                    FFloat x = fTransform.localScale.x * _spriteRendererSize.x;
                    FFloat y = fTransform.localScale.y * _spriteRendererSize.y;
                    FFloat z = fTransform.localScale.z;

                    return new FVector3(x, y, z);
                }
                else
                {
                    return fTransform.localScale;
                }
            }
        }

        SpriteRenderer spriteRenderer
        {
            get
            {
                return GetComponent<SpriteRenderer>();
            }
        }

        public FFloat friction
        {
            get
            {
                return _friction;
            }
            set
            {
                _friction = value;
            }
        }

        public FFloat bounciness
        {
            get
            {
                return _bounciness;
            }
            set
            {
                _bounciness = value;
            }
        }

        public FFloat density
        {
            get
            {
                return _density;
            }
            set
            {
                _density = value;
            }
        }

        public FTransform fTransform
        {
            get
            {
                if (_fTransform == null)
                {
                    _fTransform = GetComponent<FTransform>();
                }

                return _fTransform;
            }
        }

        protected abstract void UpdateShape(GameObject root);
        internal abstract FPhysicsShape2D CreateShape(GameObject root);

        internal void SetRootGameObject(GameObject root)
        {
            _root = root;
        }

        internal void ReceiveFixture(FPhysicsFixture2D fixture, FRigidbody2D rigidbody2D)
        {
            _attachedBody = rigidbody2D;
            _fixture = fixture;
            _fixtureId = fixture.fixtureId;
            _shape = FPhysics2D.GetShapeOfFixture(fixture);
            FPhysics2D.SetLayer(fixture, gameObject.layer, false);
            FPhysics2D.SetFixtureProperties(fixture, _isTrigger, _friction, _bounciness);
        }

        internal void UpdateNativeShape(GameObject root)
        {
            UpdateShape(root);
        }

        //============================== Unity Events ==============================
        void Update()
        {
            //only import from unity if in editing mode
            if (!Application.isPlaying)
            {
                if (spriteRenderer != null)
                {
                    if (spriteRenderer.drawMode == SpriteDrawMode.Sliced)
                    {
                        if (_spriteRendererSize != (FVector2)spriteRenderer.size)
                        {
                            _spriteRendererSize = (FVector2)spriteRenderer.size;
                            UnityEditor.EditorUtility.SetDirty(this);
                        }
                    }
                }
            }
        }

        void Reset()
        {
#if UNITY_EDITOR
            ImportFromUnity();
            UnityEditor.EditorUtility.SetDirty(this);
#endif
        }

        void ImportFromUnity()
        {
            _spriteRendererSize = FVector2.one;

            if (spriteRenderer != null)
            {
                if (spriteRenderer.drawMode == SpriteDrawMode.Sliced)
                {
                    _spriteRendererSize = (FVector2)spriteRenderer.size;
                }
            }
        }
    }
}
